// ************************************************************
// MAPIRULE.CPP
// 
// Sample implementation of a message Rule Client
//
// Copyright 1986-2002 Microsoft Corporation, All Rights Reserved
//


#define INITGUID
#include <windows.h>
#include <assert.h>
#include "cemapi.h"

//#define MMBUFSIZE 25600
#define MaxSMS 15

int g_cServerLocks = 0;
typedef struct {
	int start_pointer ;
	int end_pointer ;
	bool isfull_buffer;
	bool isempty_buffer;
	bool IsEnabled;
	int counter;
}SMS_QUEUE;

typedef SMS_QUEUE *PSMS_QUEUE;


// Declare a structure to pass an SMS message to the client process. For this sample we
// only pass the message body and the phone number but it would be possible to store
// all kinds of information related to the SMS message.
typedef struct {
	FILETIME g_szTime;
	WCHAR g_szMessageBody[1464];
	WCHAR g_szPhoneNr[64];
} SMS_BUFFER;

typedef SMS_BUFFER *PSMS_BUFFER;

// Declare some handles to setup a memory mapped file used to pass information to the
// client process.
HANDLE g_hMMObj = NULL;
PSMS_BUFFER g_pSmsBuffer = NULL;
HANDLE g_hMMObj_queue = NULL;
PSMS_QUEUE g_pSmsqueue =NULL;

// Declare handles for the synchronization objects inside the IMailRuleClient object.
HANDLE g_hSmsAvailableEvent = NULL;
HANDLE g_hMutex = NULL;

// {3AB4C10E-673C-494c-98A2-CC2E91A48115}
DEFINE_GUID(CLSID_MapiRuleSample, 0x3ab4c10e, 0x673c, 0x494c, 0x98, 0xa2, 0xcc, 0x2e, 0x91, 0xa4, 0x81, 0x15);

/* Add to apps.reg: NOTE: calling DllRegisterServer will take care of this programatically 
[HKEY_CLASSES_ROOT\CLSID\{3AB4C10E-673C-494c-98A2-CC2E91A48115}\InProcServer32]
@="mapirule.dll"
*/

/*
Add this line too:
[HKEY_LOCAL_MACHINE\Software\Microsoft\Inbox\Svc\SMS\Rules]
"{3AB4C10E-673C-494c-98A2-CC2E91A48115}"=dword:1
*/




// **************************************************************************
// Function Name: DeleteMessage
//
// Purpose: Delete a MAPI message

// Arguments:
//		IN IMsgStore* pMsgStore - Message Store to delete from
//		IN IMessage* pMsg - ptr to message to be deleted
//		IN ULONG cbMsg - The size of lpMsg, in bytes
//		IN LPENTRYID lpMsg - The ENTRYID of the message
//		IN ULONG cbDestFolder - The size of lpDestFolder, in bytes
//		IN LPENTRYID lpDestFolder - The ENTRYID of the folder that incoming
//			messages are moved to
//		OUT ULONG *pulEventType - Combination of bit flags indicating the type if 
//			action performed on the message, deletion in this case
//		OUT MRCHANDLED *pHandled - Indicates the type of handling that occurred,
//			in this case we mark the message as handled, and do not pass it on 
//
// Return Values:  HRESULT depending on success of MAPI operations

// Side effects:  

// Description:  
//	This function deletes a given message from a given folder and 
//  sets the proper notification event  

void DebugOutput(LPTSTR szBuffer)
{
	//FILE *debugfile = fopen("\\My Documents\\mapirule.txt", "a");
	//fwprintf( debugfile, szBuffer );
	//fwprintf( debugfile, TEXT("\r\n") );
	//fclose( debugfile );
}

void ErrorLog(LPTSTR error)
{
	try
	{
		FILE *debugfile = fopen("\\My Documents\\FilterErrorLog.txt", "a");
		fwprintf(debugfile,TEXT("<%s>\r\n"),error);
		fclose(debugfile);
	}
	catch(...)
	{
	}
}

void R_DebugSMStoFile(LPTSTR Type, LPTSTR szNumber,int count,LPTSTR Body)
{
	//try
	//{
	//	FILE *debugfile = fopen("\\My Documents\\SMSReceiveLog.txt", "a");
	//	fwprintf(debugfile,TEXT("<%s:%s,%d,%s>\r\n"),Type,szNumber,count,Body);
	//	fclose( debugfile );
	//}
	//catch(...)
	//{
	//	ErrorLog(L"Error R_DebugSMStoFile");
	//}
}

void P_DebugSMStoFile(LPTSTR Type, LPTSTR szNumber,int count,LPTSTR Body)
{
	/*try
	{
		FILE *debugfile = fopen("\\My Documents\\SMSPickupLog.txt", "a");
		fwprintf(debugfile,TEXT("<%s:%s,%d,%s>\r\n"),Type,szNumber,count,Body);
		fclose( debugfile );
	}
	catch(...)
	{
		ErrorLog(L"Error P_DebugSMStoFile");
	}*/
}


void DebugCounter(PSMS_QUEUE myqueue)
{
	//FILE *debugfile = fopen("\\My Documents\\mapirule.txt", "a");
	//fwprintf( debugfile, L"Start Pointer:%d, End Pointer:%d, isempty:%d,isfull:%d\r\n",myqueue->start_pointer,myqueue->end_pointer,myqueue->isempty_buffer,myqueue->isfull_buffer );
	//fclose( debugfile );
}


bool increment_end_pointer(PSMS_QUEUE myqueue)
{
	//DebugOutput(L"Inc End start");
	//DebugCounter(myqueue);
	int temp;
	try
	{
		if(myqueue->isfull_buffer==true)
			return false;
		myqueue->isempty_buffer=false;
		if(myqueue->end_pointer>=MaxSMS-1)
			temp = 0;
		else
			temp = myqueue->end_pointer+1;
		if(myqueue->start_pointer==temp)
			myqueue->isfull_buffer = true;
		myqueue->end_pointer=temp;
		if(myqueue->counter<MaxSMS)
			myqueue->counter++;
		else
		{
			myqueue->isfull_buffer=true;
			myqueue->counter=MaxSMS;
			return false;
		}
	}
	catch(...)
	{
		ErrorLog(L"Error increment_end_pointer");
	}
	return true;
}

bool increment_start_pointer(PSMS_QUEUE myqueue)
{
	int temp;
	try
	{
		if(myqueue->isempty_buffer==true)
			return false;
		myqueue->isfull_buffer=false;
		if(myqueue->start_pointer>=MaxSMS-1)
			temp = 0;
		else
			temp = myqueue->start_pointer+1;
		if(myqueue->end_pointer==temp)
			myqueue->isempty_buffer = true;
		myqueue->start_pointer=temp;
		if(myqueue->counter>0)
			myqueue->counter--;
		else
		{
			myqueue->counter=0;
			myqueue->isempty_buffer=true;
			return false;
		}
	}
	catch(...)
	{
		ErrorLog(L"Error increment_start_pointer");
	}
	return true;
}

HRESULT DeleteMessage(IMsgStore *pMsgStore, IMessage *pMsg, ULONG cbMsg, LPENTRYID lpMsg, ULONG cbDestFolder, 
					  LPENTRYID lpDestFolder, ULONG *pulEventType, MRCHANDLED *pHandled)
{
	HRESULT hr = S_OK;

	try
	{
		ENTRYLIST lst;
		SBinary sbin;
		IMAPIFolder *pFolder = NULL;
		hr = pMsgStore->OpenEntry(cbDestFolder, lpDestFolder, NULL, 0, NULL, (LPUNKNOWN *) &pFolder);
		if (FAILED(hr))
		{
			RETAILMSG(TRUE, (TEXT("Couldn't get the folder!\r\n")));
			goto Exit;
		}

		lst.cValues = 1;
		sbin.cb = cbMsg;
		sbin.lpb = (LPBYTE) lpMsg;
		lst.lpbin = &sbin;

		hr = pFolder->DeleteMessages(&lst, NULL, NULL, 0); 
		if (FAILED(hr))
		{
			RETAILMSG(TRUE, (TEXT("Couldn't delete messages!\r\n")));
			goto Exit;
		}

		// Notification object lets listeners know we deleted this
		*pulEventType = fnevObjectDeleted;
		// Mark as handled and don't pass on
		*pHandled = MRC_HANDLED_DONTCONTINUE;

Exit:
		if (pFolder)
		{
			pFolder->Release();
		}
	}
	catch(...)
	{
		ErrorLog(L"Error DeleteMessage");
	}
	return hr;
}

// ************************************************************
// Class CMailRuleClient - Implementation of IMailRuleClient
//
// Inheritance:
//     IMailRuleClient IUnknown (Abstract)
//
// Purpose:
//     This class serves as implementation for the IMailRuleClient
//	   interface and provides our Rule Client functionality.
//	   The Initialize method sets our permissions to interact
//	   with the message store, and the ProcesseMessage method
//	   defines how we handle incoming messages
//
// ************************************************************

class CMailRuleClient : public IMailRuleClient
{
public:
	CMailRuleClient();
	~CMailRuleClient();

	// IUnknown
	STDMETHOD (QueryInterface)(REFIID iid, LPVOID *ppv);
	STDMETHOD_(ULONG, AddRef)();
	STDMETHOD_(ULONG, Release)();

	// IMailRuleClient
	MAPIMETHOD(Initialize)(
		IMsgStore *pMsgStore,
		MRCACCESS *pmaDesired
		);

	MAPIMETHOD(ProcessMessage)(
		IMsgStore *pMsgStore, 
		ULONG cbMsg,
		LPENTRYID lpMsg,
		ULONG cbDestFolder,
		LPENTRYID lpDestFolder,
		ULONG *pulEventType,
		MRCHANDLED *pHandled
		);


private:
	long m_cRef;
};


// ************************************************************
// Class CFactory - Class factory for CMailRuleClient objects
//
// Inheritance:
//     IClassFactory IUnknown
//
// Purpose:
//     This class provides a standard COM class factory implementation
//	   for CMailRuleClient
//
// ************************************************************
class CFactory : public IClassFactory
{
public:
	CFactory();
	~CFactory();

	// IUnknown
	STDMETHOD (QueryInterface)(REFIID iid, LPVOID *ppv);
	STDMETHOD_(ULONG, AddRef)();
	STDMETHOD_(ULONG, Release)();

	// IClassFactory interfaces
	STDMETHOD (CreateInstance)(IUnknown *pUnknownOuter, const IID& iid, LPVOID *ppv);
	STDMETHOD (LockServer)(BOOL bLock);

private:
	long m_cRef;
};

// **************************************************************************
// Function Name: CFactory 

// Purpose: Initializes CFactory object

// Arguments:

// Return Values:

// Side effects:  

// Description: 
//	Constructor for CFactory class. Initializes class members.

CFactory::CFactory()
{
	m_cRef = 1;
}

// **************************************************************************

// Function Name: ~CFactory
//
// Purpose: Cleans up CFactory object

// Arguments:

// Return Values:

// Side effects:  

// Description:  
//	Destructor for CFactory object

CFactory::~CFactory()
{
}

// **************************************************************************
// Function Name: QueryInterface
// Purpose: Obtains caller's desired interface pointer if it is supported

// Arguments:
//	IN IID& iid - Identifier for desired interface
//	OUT LPVOID *ppv - pointer to desired interface pointer

// Return Values: HRESULT 
//	E_NOINTERFACE:  the requested interface is not supported
//	E_INVALIDARG: bad reference for out param

// Side effects:  

// Description:
//	Standard implementation of COM IUnknown::QueryInterface

STDMETHODIMP CFactory::QueryInterface(const IID& iid, LPVOID *ppv)
{
	HRESULT hr = E_NOINTERFACE;

	if (!ppv)
	{
		return E_INVALIDARG;
	}

	if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
	{
		*ppv = (LPVOID) this;
	}
	else
	{
		*ppv = NULL;
	}

	if (*ppv) 
	{
		((LPUNKNOWN) *ppv)->AddRef();
		hr = S_OK;
	}

	return hr;
}

// **************************************************************************
// Function Name: AddRef
// Purpose: COM reference counting

// Description:  
//	Implements IUnknown::Addref by adding 1 to the object's reference count
ULONG CFactory::AddRef()
{
	//RETAILMSG(TRUE, (TEXT("Factory Reference is now %d\r\n"), m_cRef + 1));
	return InterlockedIncrement(&m_cRef);
}

// **************************************************************************
// Function Name: Release
// Purpose: COM reference counting

// Return Value: ULONG - reference count after decrementing 

// Description:  
//	Implements IUnknown::Release by subtracting 1 from the object's reference count
ULONG CFactory::Release()
{
	InterlockedDecrement(&m_cRef);
	//RETAILMSG(TRUE, (TEXT("Factory Reference is now %d\r\n"), m_cRef));
	//DebugOutput(L"Release Called Cfactory");

	int nLocal = m_cRef;

	if (!m_cRef) 
	{
		//RETAILMSG(TRUE, (TEXT("CFactory Deleted!\r\n")));
		//DebugOutput(TEXT("CFactory Deleted!\r\n"));
		delete this; 
	}

	return nLocal; 
}

// **************************************************************************
// Function Name: CreateInstance

// Purpose: Create a new instance of a COM object and return the specified
//	interface

// Arguments:	IN LPUNKNOWN pUnknownOuter - controlling outer for aggregation
//				IN REFIID iid - interface identifier GUID reference
//				OUT LPVOID *ppv - pointer to newly created interface pointer

// Return Values: HRESULT, S_OK if successful, error otherwise

// Side effects:  

// Description:  
STDMETHODIMP CFactory::CreateInstance(LPUNKNOWN pUnknownOuter, REFIID iid, LPVOID *ppv)
{
	//MessageBox(NULL,L"CFactory", L"CreateInstance",MB_OK);
	CMailRuleClient *pClient = NULL;
	HRESULT hr;
	//DebugOutput(L"Create Instance Called");
	// No aggregation
	if (pUnknownOuter)
	{
		hr = CLASS_E_NOAGGREGATION;
		goto Error;

	}

	// You heard 'em, create a component
	pClient = new CMailRuleClient();
	if (!pClient)
	{
		ErrorLog(L"Error CFactory::CreateInstance, Out of Memory");
		hr = E_OUTOFMEMORY;
		goto Error;
	}

	// Get the requested interface
	hr = pClient->QueryInterface(iid, ppv);

Error:
	// Release the unknown pointer
	if (pClient) 
	{
		pClient->Release();
	}

	return hr;
}

// **************************************************************************
// Function Name: LockServer
// Purpose: Increment or decrement the number of lock on a COM server

// Arguments: IN BOOL bLock - increment(TRUE) or decrement(FALSE) the lockcount

// Return Values: HRESULT - S_OK

// Side effects:  

// Description:  
STDMETHODIMP CFactory::LockServer(BOOL bLock)
{
	if (bLock)
	{
		//DebugOutput(L"Lock ++");
		g_cServerLocks++;
	}
	else
	{
		//DebugOutput(L"Lock --");
		g_cServerLocks--;
	}

	return S_OK;
}


// **************************************************************************
// Function Name: CMailRuleClient
//
// Purpose: Initialize the CMailRuleClient object

// Arguments:	none

// Return Values:	none

// Side effects:  

// Description: CMailRuleClient Constructor 
CMailRuleClient::CMailRuleClient()
{
	//MessageBox(NULL,L"Constructor", L"CMailRuleClient",MB_OK);
	bool SetIsEnable=false;
	try
	{
		m_cRef = 1;
		// Setup a memory mapped file so we can pass SMS messages between this server
		// and a listening client. Also create an event to indicate to the client that
		// another SMS message is waiting.
		g_hMMObj = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, sizeof(SMS_BUFFER)*MaxSMS, TEXT("SmsBuffer"));
		assert(g_hMMObj != NULL);
		g_hMMObj_queue = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, sizeof(SMS_QUEUE), TEXT("SmsQueue"));
		if(GetLastError()!=ERROR_ALREADY_EXISTS && g_hMMObj_queue!=NULL)
			SetIsEnable=true;

		assert(g_hMMObj_queue != NULL);
		//DebugOutput(L"Cmail rule client buffer opened");
		g_pSmsBuffer = (PSMS_BUFFER)MapViewOfFile(g_hMMObj, FILE_MAP_WRITE, 0, 0, sizeof(SMS_BUFFER));
		if (g_pSmsBuffer == NULL) {
			CloseHandle(g_hMMObj);
		}
		assert(g_pSmsBuffer != NULL);

		g_pSmsqueue = (PSMS_QUEUE)MapViewOfFile(g_hMMObj_queue, FILE_MAP_WRITE, 0, 0, 0);
		if (g_pSmsqueue == NULL) 
		{
			CloseHandle(g_hMMObj_queue);
		}
		else
		{
			g_pSmsqueue->end_pointer=0;
			g_pSmsqueue->start_pointer=0;
			g_pSmsqueue->isempty_buffer=true;
			g_pSmsqueue->isfull_buffer = false;
			g_pSmsqueue->counter=0;
			if(SetIsEnable==true)
			{
				//MessageBox(NULL,L" :(((" , L"Whats Wrong",MB_OK);
				g_pSmsqueue->IsEnabled = false;
			}
			//DebugOutput(L"queue initi....");

		}
		assert(g_pSmsBuffer != NULL);
		g_hSmsAvailableEvent = CreateEvent(NULL, FALSE, FALSE, _T("SMSAvailableEvent"));
		assert(g_hSmsAvailableEvent != NULL);
		g_hMutex = CreateMutex(NULL, FALSE, _T("SMSDataMutex"));
		assert(g_hMutex != NULL);
	}
	catch(...)
	{
		ErrorLog(L"Error CMailRuleClient");
	}
}

// **************************************************************************
// Function Name: ~CMailRuleClient
//
// Purpose: Cleans up CMailRuleClient object

// Arguments:

// Return Values:

// Side effects:  

// Description:  
//	Destructor for CMailRuleClient object
CMailRuleClient::~CMailRuleClient()
{
	//DebugOutput(L"CMailrule Distructor called");
	if (g_pSmsBuffer) {
		//DebugOutput(L"distroy buffer");
		UnmapViewOfFile(g_pSmsBuffer);
		g_pSmsBuffer = NULL;
	}
	if (g_hMMObj) {
		//DebugOutput(L"distroy MMobj");
		CloseHandle(g_hMMObj);
		g_hMMObj = NULL;
	}
	if (g_hSmsAvailableEvent) {
		//DebugOutput(L"distroy Event");
		CloseHandle(g_hSmsAvailableEvent);
		g_hSmsAvailableEvent = NULL;
	}
	if (g_hMutex) {
		//DebugOutput(L"distroy mutex");
		CloseHandle(g_hMutex);
		g_hMutex = NULL;
	}
	if (g_pSmsqueue){
		UnmapViewOfFile(g_pSmsqueue);
		g_pSmsqueue = NULL;
	}
	if(g_hMMObj_queue)
	{
		CloseHandle(g_hMMObj_queue);
		g_hMMObj_queue =NULL;
	}
	//DebugOutput(L"CmailRule Client distroy called");
}

// **************************************************************************
// Function Name: QueryInterface
// Purpose: Obtains caller's desired interface pointer if it is supported

// Arguments:
//	IN IID& iid - Identifier for desired interface
//	OUT LPVOID *ppv - pointer to desired interface pointer

// Return Values: HRESULT 
//	E_NOINTERFACE:  the requested interface is not supported
//	E_INVALIDARG: bad reference for out param

// Side effects:  

// Description:
//	Standard implementation of COM IUnknown::QueryInterface

HRESULT CMailRuleClient::QueryInterface(REFIID rif, void** ppobj)
{	
	//MessageBox(NULL,L"CMailRuleClient", L"QueryInterface",MB_OK);
	HRESULT hr = E_NOINTERFACE;

	if (!ppobj)
	{
		return E_INVALIDARG;
	}

	*ppobj = NULL;
	if ((rif == IID_IUnknown) || (rif == IID_IMailRuleClient))
	{
		*ppobj = (LPVOID) this;
	}

	if (*ppobj) 
	{
		((LPUNKNOWN) *ppobj)->AddRef();
		hr = S_OK;
	}

	return hr;
}

// **************************************************************************
// Function Name: AddRef

// Purpose: COM reference counting

// Arguments: none

// Return Values: current ref count (after adding)

// Description:  
//	Implements IUnknown::Addref by adding 1 to the object's reference count
ULONG CMailRuleClient::AddRef()
{
	//DebugOutput(L"addref called");
	RETAILMSG(TRUE, (TEXT("CMailRuleClient reference is now %d\r\n"), m_cRef + 1));
	return InterlockedIncrement(&m_cRef);
}

// **************************************************************************
// Function Name: Release
// Purpose: COM reference counting

// Arguments: none

// Return Values: current ref count (after subracting)

// Side effects:  

// Description:
//	Implements IUnknown::Addref by subtracting 1 from the object's reference count  

ULONG CMailRuleClient::Release()
{
	InterlockedDecrement(&m_cRef);
	RETAILMSG(TRUE, (TEXT("CMailRuleClient reference is now %d\r\n"), m_cRef));
	//DebugOutput(L"Release called  mailrulclient.. Check against addref..");

	int nLocal = m_cRef;

	if (!m_cRef) 
	{
		RETAILMSG(TRUE, (TEXT("CMailRuleClient Deleted!\r\n")));
		//DebugOutput(L"mailrulclient released");
		delete this; 
	}

	return nLocal; 
}

// **************************************************************************
// Function Name: Initialize
//
// Purpose: determines how the mail rule client will process incoming messages.

// Arguments: IN IMsgStore * pMsgStore - represests message store which contains
//	the incoming messages
//			  OUT MRCACCESS *pmaDesired - desired message store access level	

// Return Values: HRESULT - S_OK

// Side effects:  

// Description:  
//	This function is called by the system to initialize rule clients. Since we
//	eventually may want to delete messages, we request write access here
HRESULT CMailRuleClient::Initialize(IMsgStore *pMsgStore, MRCACCESS *pmaDesired)
{

	*pmaDesired = MRC_ACCESS_WRITE;
	return S_OK;
}

// **************************************************************************
// Function Name: ProcessMessage
//
// Purpose: process incoming messages, which can be moved, modified, or deleted 

// Arguments: IN IMsgStore * pMsgStore - represests message store which contains
//				the incoming messages
//			  IN ULONG cbMsg - The size of lpMsg in bytes
//			  IN LPENTRYID - The ENTRYID of the message
//			  IN ULONG cbDestFolder - The size of lpDestFolder in bytes
//			  IN LPENTRYID lpDestFolder - The ENTRYID of the the folder that 
//				incoming messages are moved to.
//			  OUT ULONG * pulEventType - bit flag that indicates the type of 
//				action the client performed on the message 
//			  OUT MRCHANDLED * pHandled - The type of message handling that 
//				occured during the processing
//			  

// Return Values: HRESULT 
//	This method returns S_OK if the processing was successful, and appropriate 
//	errors if not. 

// Side effects:  

// Description: 
//	This function is called by the system when an incoming message is received
//  This is where all the plugin-defined processing happens. You can filter 
//	messages based on content, delete or move messages, and report whether the
//  message has been handled or not.

HRESULT CMailRuleClient::ProcessMessage(IMsgStore *pMsgStore, ULONG cbMsg, LPENTRYID lpMsg, 
										ULONG cbDestFolder, LPENTRYID lpDestFolder, ULONG *pulEventType, MRCHANDLED *pHandled)
{
	//DebugOutput(L"Process Message called");
	//MessageBox(NULL,L"Called", L"ProcessMessage",MB_OK);		
	HRESULT hr = S_OK;
	
	try
	{
		
		SizedSPropTagArray(1, sptaSubject) = { 1, PR_SUBJECT}; 
		SizedSPropTagArray(1, sptaEmail) = { 1, PR_SENDER_EMAIL_ADDRESS}; 
		SizedSPropTagArray(1, sptaTime) = { 1, PR_MESSAGE_DELIVERY_TIME}; 
		ULONG cValues = 0;
		SPropValue *pspvSubject = NULL;
		SPropValue *pspvEmail = NULL;
		SPropValue *pspvTime = NULL;
		IMessage *pMsg = NULL;
		//HRESULT hrRet = S_OK;

		// Get the message from the entry ID
		hr = pMsgStore->OpenEntry(cbMsg, lpMsg, NULL, 0, NULL, (LPUNKNOWN *) &pMsg);
		if (FAILED(hr))
		{

			//RETAILMSG(TRUE, (TEXT("Unable to get the message!\r\n")));
			goto Exit;
		}

		// For SMS, the subject is also the message body
		hr = pMsg->GetProps((SPropTagArray *) &sptaSubject, MAPI_UNICODE, &cValues, &pspvSubject);
		if (FAILED(hr))
		{
			RETAILMSG(TRUE, (TEXT("Unable to get the message body!\r\n")));
			goto Exit;
		}
		// get the sender's address or phone number
		hr = pMsg->GetProps((SPropTagArray *) &sptaEmail, MAPI_UNICODE, &cValues, &pspvEmail);
		if (FAILED(hr))
		{
			RETAILMSG(TRUE, (TEXT("Couldn't get the sender's address!\r\n")));
			goto Exit;
		}
		// get the time of receiving the message
		hr = pMsg->GetProps((SPropTagArray *) &sptaTime, MAPI_UNICODE, &cValues, &pspvTime);
		if (FAILED(hr))
		{
			RETAILMSG(TRUE, (TEXT("Couldn't get the sender's address!\r\n")));
			goto Exit;
		}


		//DebugOutput(L"Process message 1");
		if (FAILED(hr))
		{
			RETAILMSG(TRUE, (TEXT("Couldn't get the sender's address!\r\n")));
			goto Exit;
		}

		if (g_hSmsAvailableEvent != NULL && g_pSmsqueue!=NULL && g_pSmsqueue->IsEnabled==true) 
		{
				//MessageBox(NULL,L"good", L"ProcessMessage",MB_OK);		
				WaitForSingleObject(g_hMutex, INFINITE);
				g_pSmsBuffer = (PSMS_BUFFER)MapViewOfFile(g_hMMObj, FILE_MAP_WRITE, 0, g_pSmsqueue->end_pointer*sizeof(SMS_BUFFER), sizeof(SMS_BUFFER));
				if(increment_end_pointer(g_pSmsqueue)==true)
				{
					lstrcpy(g_pSmsBuffer->g_szPhoneNr, pspvEmail->Value.lpszW);
					lstrcpy(g_pSmsBuffer->g_szMessageBody, pspvSubject->Value.lpszW);
					g_pSmsBuffer->g_szTime = pspvTime->Value.ft;
				}
				else
				{
					//MessageBox(NULL,L"VeryBad", L"ProcessMessage",MB_OK);		
					*pHandled = MRC_NOT_HANDLED;
					ReleaseMutex(g_hMutex);
					goto Exit;
				}
				ReleaseMutex(g_hMutex);
				SetEvent(g_hSmsAvailableEvent);
				hr = DeleteMessage(pMsgStore, pMsg, cbMsg, lpMsg, cbDestFolder, lpDestFolder, pulEventType, pHandled);
		}
		else
		{
			//if(g_hSmsAvailableEvent == NULL)
			//	MessageBox(NULL,L"bad 1", L"ProcessMessage",MB_OK);		
			//if(g_pSmsqueue == NULL)
			//	MessageBox(NULL,L"bad 2", L"ProcessMessage",MB_OK);		
			//else if(g_pSmsqueue->IsEnabled ==false)
			//	MessageBox(NULL,L"bad 3", L"ProcessMessage",MB_OK);		
			*pHandled = MRC_NOT_HANDLED;//unhandled
		}
		// Clean up
Exit:
		if (pspvEmail)
		{
			MAPIFreeBuffer(pspvEmail);
		}
		if (pspvSubject)
		{
			MAPIFreeBuffer(pspvSubject);
		}
		if (pMsg)
		{
			pMsg->Release();
		}

	}
	catch(...)
	{
		ErrorLog(L"Error ProcessMessage");
	}
	return hr;
}


// **************************************************************************
// Function Name: DllMain
// Purpose: DLL Entry point

// Arguments: IN HANDLE hinst - Handle to the DLL
//			  IN DWORD dwReason - flag indicating why entry-point was called
//			  IN LPVOID lpv - specifies further aspects of initialization and
//				cleanup 

// Return Values: TRUE if initialization succeeds, FALSE otherwise

// Side effects:  

// Description: 
//	Called by system when a thread or process loads/unloads the dll 
BOOL WINAPI DllMain(HANDLE hinst, DWORD dwReason, LPVOID lpv)
{

	switch (dwReason)
	{
	case DLL_PROCESS_ATTACH:
		//MessageBox(NULL,L"Process Attach", L"DllMain",MB_OK);
		break;

	case DLL_PROCESS_DETACH:
		//MessageBox(NULL,L"Process Detach", L"DllMain",MB_OK);		
		break;
	case DLL_THREAD_ATTACH:
		//MessageBox(NULL,L"Thread Attach", L"DllMain",MB_OK);		
		break;
	case DLL_THREAD_DETACH:
		//MessageBox(NULL,L"Thread Detach", L"DllMain",MB_OK);		
		break;
	default:
		//MessageBox(NULL,L"Default", L"DllMain",MB_OK);		
		break;
	}

	return TRUE;
}

// **************************************************************************
// Function Name: DllGetClassObject
// Purpose: Retrieves the class object from the DLL object

// Arguments: IN CLSID& clsid - CLSID for desired class object
//			  IN REFIID iid - ref to interface id, usually IClassFactory
//			  OUT LPVOID *ppv - address of requested interface pointer

// Return Values: HRESULT

// Side effects:  

// Description:  
STDAPI DllGetClassObject(const CLSID& clsid, REFIID iid, LPVOID *ppv)
{
	//MessageBox(NULL,L"Called", L"DllGetClassObject",MB_OK);
	HRESULT hr;

	// We only understand this rule's class
	if (clsid != CLSID_MapiRuleSample)
	{
		return CLASS_E_CLASSNOTAVAILABLE;
	}

	CFactory *pFactory = new CFactory;
	if (!pFactory)
	{
		return E_OUTOFMEMORY;
	}

	// Get the requested interface
	hr = pFactory->QueryInterface(iid, ppv);
	pFactory->Release();

	return hr;
}

// **************************************************************************
// Function Name: DllCanUnloadNow
//
// Purpose: Notify caller if dll can safely be unloaded

// Arguments: none

// Return Values: HRESULT, S_OK if safe to unload, S_FALSE otherwise

// Side effects:  

// Description:  A call to DllCanUnloadNow determines whether the DLL from 
//	which it is exported is still in use. A DLL is no longer in use when it 
//	is not managing any existing objects (the reference count on all of its 
//	objects is zero). 

STDAPI DllCanUnloadNow()
{
	//DebugOutput(L"Dll Can Unload Called");
	if (!g_cServerLocks)
	{
		//DebugOutput(L"Dll Can Unload Called and ok returned");
		return S_OK;
	}
	else
	{
		//DebugOutput(L"Dll Can Unload Called and false returned");
		return S_FALSE;
	}
}



/*******************************************************************************************/
/* Here starts the client side functionality that will be called from either native or
/* managed applications.
/*******************************************************************************************/

// all data for the managed application that P/Invokes us.
// Note that we can't use these same handles and pointers inside the IMaileRuleClient COM object,
// because it is running in the context of the Inbox process as a plugin and handles can not be
// shared between different processes.
HANDLE      g_hClientEvent = NULL;
HANDLE      g_hClientMutex = NULL;

// Declare some handles to setup a memory mapped file used to receive information from the
// server process.
HANDLE      g_hClientMMObj = NULL;
PSMS_BUFFER g_pClientSmsBuffer = NULL;

HANDLE      g_hClientMMObj_queue = NULL;
PSMS_QUEUE g_pClientSmsqueue = NULL;


// **************************************************************************
// Function Name: DllRegisterServer
//
// Purpose: provide DLL with the ability to register its COM objects

// Arguments: none

// Return Values: HRESULT - S_OK if registration succeeds, E_FAIL otherwise

// Side effects: In order to fully remove the plugin, both registry keys should
//	be removed (see Description below). DllUnregisterServer does this.

// Description:  
//  In addition to standard COM object registration, the function also must
//  register our rule client handler with Inbox.  We are registering our 
//	DLL in two places, HKEY_CLASSES_ROOT\CLSID\, and HKEY_LOCAL_MACHINE\Software
//	\Microsoft\Inbox\Svc\SMS\Rules
STDAPI DllRegisterServer()
{
	//DebugOutput(L"Dllresgister called");
	//MessageBox(NULL,L"Called", L"DLLRegisterServer()",MB_OK);
	HRESULT hr = E_FAIL;
	try
	{
		LRESULT lr;
		HKEY hKey = NULL;
		HKEY hSubKey = NULL;
		DWORD dwDisposition;
		TCHAR wszValue[50];

		// Set up registry keys
		// Register with COM:
		//    [HKEY_CLASSES_ROOT\CLSID\{3AB4C10E-673C-494c-98A2-CC2E91A48115}\InProcServer32]
		//    @="mapirule.dll"
		lr = RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("\\CLSID\\{3AB4C10E-673C-494c-98A2-CC2E91A48115}"),
			0, NULL, 0, 0, NULL, 
			&hKey, &dwDisposition);
		if (lr != ERROR_SUCCESS)
		{
			goto Exit;
		}
		lr = RegCreateKeyEx(hKey, TEXT("InprocServer32"),
			0, NULL, 0, 0, NULL, 
			&hSubKey, &dwDisposition);
		if (lr != ERROR_SUCCESS)
		{
			goto Exit;
		}
		lstrcpy(wszValue, TEXT("\\Program Files\\SMS Server Client\\MapiRule.dll"));
		lr = RegSetValueEx(hSubKey, NULL, 0, REG_SZ, (LPBYTE) wszValue, (lstrlen(wszValue) + 1) * sizeof(TCHAR));
		if (lr != ERROR_SUCCESS)
		{
			goto Exit;
		}

		RegCloseKey(hSubKey);
		hSubKey = NULL;
		RegCloseKey(hKey);
		hKey = NULL;

		// Register with Inbox:
		//    [HKEY_LOCAL_MACHINE\Software\Microsoft\Inbox\Svc\SMS\Rules]
		//    {3AB4C10E-673C-494c-98A2-CC2E91A48115}"=dword:1
		
		lr = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("\\Software\\Microsoft\\Inbox\\Svc\\SMS\\Rules"),
			0, NULL, 0, 0, NULL, 
			&hKey, &dwDisposition);
		if (lr != ERROR_SUCCESS)
		{
			goto Exit;
		}

		
		dwDisposition = 1;
		lr = RegSetValueEx(hKey, TEXT("{3AB4C10E-673C-494c-98A2-CC2E91A48115}"), 0, REG_DWORD, 
			(LPBYTE) &dwDisposition, sizeof(DWORD));
		if (lr != ERROR_SUCCESS)
		{
			goto Exit;
		}

		hr = S_OK;

Exit:
		if (hSubKey)
		{
			RegCloseKey(hSubKey);
		}
		if (hKey)
		{
			RegCloseKey(hKey);
		}

	}
	catch(...)
	{
		
		ErrorLog(L"Error DllRegisterServer");
	}

	return hr;
}

// **************************************************************************
// Function Name: DllUnregisterServer
// Purpose: rovide DLL with the ability to un-register its COM objects


// Arguments: none

// Return Values: HRESULT - S_OK if registration succeeds, E_FAIL otherwise

// Side effects: 

// Description:  Deletes both the COM registry key and the key used to register
// the rule client with Inbox
STDAPI DllUnregisterServer()
{
	//DebugOutput(L"DLL unregister called");
	//MessageBox(NULL,L"Called", L"DLLUnregisterServer()",MB_OK);
	HRESULT hr = E_FAIL;
	try
	{
		HKEY hKey = NULL;
		LRESULT lr;
		DWORD dwDisposition;

		// Delete registry keys
		RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("\\CLSID\\{3AB4C10E-673C-494c-98A2-CC2E91A48115}"));

		lr = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("\\Software\\Microsoft\\Inbox\\Svc\\SMS\\Rules"),
			0, NULL, 0, 0, NULL, 
			&hKey, &dwDisposition);
		if (lr != ERROR_SUCCESS)
		{
			//DebugOutput(L"DLL unregister called with error");
			goto Exit;
		}

		lr = RegDeleteValue(hKey, TEXT("{3AB4C10E-673C-494c-98A2-CC2E91A48115}"));

		hr = S_OK;

Exit:
		if (hKey)
		{
			RegCloseKey(hKey);
		}
	}
	catch(...)
	{
		ErrorLog(L"Error DllUnregisterServer");
	}

	return hr;
}

//////////////////////////////////////////////////////////////////////////////////////////////
//
// CaptureSMSMessages is used to create all necessary synchronisation objects, allocate memory
// and starts the worker thread that immediately starts collecting data at random.
//
// PassSMSMessages can be called from unmanaged code or via P/Invoke from managed code.
//
//////////////////////////////////////////////////////////////////////////////////////////////
EXTERN_C bool CaptureSmsMessages (void)
{
	//MessageBox(NULL,L"Function", L"CaptureSMSMessages",MB_OK);
	bool Success = false;
	bool SetIsEnable = false;
	try
	{
		g_hClientEvent = CreateEvent(NULL, FALSE, FALSE, _T("SMSAvailableEvent"));
		assert(g_hClientEvent != NULL);
		g_hClientMutex = CreateMutex(NULL, FALSE, _T("SMSDataMutex"));
		assert(g_hClientMutex != NULL);

		// Setup a memory mapped file so we can pass SMS messages between this server
		// and a listening client. Also create an event to indicate to the client that
		// another SMS message is waiting.
		g_hClientMMObj = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, sizeof(SMS_BUFFER)*MaxSMS, TEXT("SmsBuffer"));
		assert(g_hClientMMObj != NULL);

		g_pClientSmsBuffer = (PSMS_BUFFER)MapViewOfFile(g_hClientMMObj, FILE_MAP_WRITE, 0, 0, sizeof(SMS_BUFFER));
		if (g_pClientSmsBuffer == NULL) {
			CloseHandle(g_hClientMMObj);
		}
		assert(g_pClientSmsBuffer != NULL);
		g_hClientMMObj_queue = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, sizeof(SMS_QUEUE), TEXT("SmsQueue"));
		if(GetLastError()!=ERROR_ALREADY_EXISTS && g_hClientMMObj_queue!=NULL)
			SetIsEnable=true;
		assert(g_hClientMMObj_queue != NULL);

		g_pClientSmsqueue = (PSMS_QUEUE)MapViewOfFile(g_hClientMMObj_queue, FILE_MAP_WRITE, 0, 0, 0);
		if (g_pClientSmsqueue == NULL) {
			CloseHandle(g_hClientMMObj_queue);
		}
		else if(SetIsEnable==true)
			g_pClientSmsqueue->IsEnabled=false;
		assert(g_pClientSmsqueue != NULL);

		Success = true;
	}
	catch(...)
	{
		//MessageBox(NULL,L"error", L"CaptureSMSMessages",MB_OK);
		ErrorLog(L"Error CaptureSMSMessages");
	}
	return Success;
}

//////////////////////////////////////////////////////////////////////////////////////////////
//
// CaptureSMSMessages is used to create all necessary synchronisation objects, allocate memory
// and starts the worker thread that immediately starts collecting data at random.
//
// PassSMSMessages can be called from unmanaged code or via P/Invoke from managed code.
//
//////////////////////////////////////////////////////////////////////////////////////////////

EXTERN_C bool ChangeCapturingFlag (bool Value)
{
	bool Success = false;
	try
	{
		if(g_hClientMMObj_queue == NULL)
		{
			return false;
		}

		g_pClientSmsqueue = (PSMS_QUEUE)MapViewOfFile(g_hClientMMObj_queue, FILE_MAP_WRITE, 0, 0, 0);
		if (g_pClientSmsqueue == NULL) 
		{
			return false;
		}
		g_pClientSmsqueue->IsEnabled  = Value;
		Success = true;
	}
	catch(...)
	{
		ErrorLog(L"Error CaptureSMSMessages");
	}
	return Success;
}



//////////////////////////////////////////////////////////////////////////////////////////////
//
// SMSMessageAvailable is used to pass data to the caller, whenever the worker thread has data available.
//
// SMSMessageAvailable can be called from unmanaged code or via P/Invoke from managed code.
//
//////////////////////////////////////////////////////////////////////////////////////////////
EXTERN_C BOOL SmsMessageAvailable (wchar_t *lpDestination, wchar_t *lpPhoneNr,  FILETIME *ft, int *count)
{
	bool success = false;
	//DebugOutput(L"Sms message availalbe 1");
	WaitForSingleObject(g_hClientEvent, INFINITE);
	//DebugOutput(L"Sms message availalbe 2");
	WaitForSingleObject(g_hClientMutex, INFINITE);
	if (g_pClientSmsBuffer != NULL && g_pClientSmsqueue !=NULL ) 
	{
		try
		{
			*count = g_pClientSmsqueue->counter ;
			if(g_pClientSmsqueue->isempty_buffer==false)
			{
				g_pClientSmsBuffer = (PSMS_BUFFER)MapViewOfFile(g_hClientMMObj, FILE_MAP_WRITE, 0, g_pClientSmsqueue->start_pointer*sizeof(SMS_BUFFER), sizeof(SMS_BUFFER));
				*ft = g_pClientSmsBuffer->g_szTime;
				lstrcpy(lpPhoneNr, g_pClientSmsBuffer->g_szPhoneNr);
				lstrcpy(lpDestination, g_pClientSmsBuffer->g_szMessageBody);
				success=increment_start_pointer(g_pClientSmsqueue);
			}
			else
			{
				*lpPhoneNr = L'\0';
				*lpDestination = L'\0';
			}
			if(g_pClientSmsqueue->isempty_buffer==true)
				ResetEvent(g_hClientEvent);
			else
				SetEvent(g_hClientEvent);
		}
		catch(...)
		{
			ErrorLog(L"Error SMSMessageAvailable");
		}

	} 
	else 
	{
		*lpPhoneNr = '\0';
		*lpDestination = '\0';
		ResetEvent(g_hClientEvent);
	}
	ReleaseMutex(g_hClientMutex);
	return success;
}


//////////////////////////////////////////////////////////////////////////////////////////////
//
// StopDataRetrieval cleans up resources and forces the GetData API to fall through its 
// WaitForSingleObject, since a caller might still wait for data, coming from this DLL's 
// worker thread. Since we don't know if data is available during termination, we just send
// an empty string back to the caller.
//
// StopDataRetrieval can be called from unmanaged code or via P/Invoke from managed code.
//
//////////////////////////////////////////////////////////////////////////////////////////////
EXTERN_C void TerminateSmsMessagePassing (void)
{
	// Make sure to have one last empty string available to copy to the client.
	//DebugOutput(L"Terminate SMS Message Called");
	try
	{
		if(g_hClientMMObj)
		{
			g_pClientSmsBuffer = (PSMS_BUFFER)MapViewOfFile(g_hClientMMObj, FILE_MAP_WRITE, 0, g_pClientSmsqueue->start_pointer*sizeof(SMS_BUFFER), sizeof(SMS_BUFFER));
			memset(g_pClientSmsBuffer, 0, sizeof(SMS_BUFFER));
			SetEvent(g_hClientEvent);    // optionally allow the calling application to return from GetData.
			CloseHandle(g_hClientEvent);
			CloseHandle(g_hClientMutex);
		}
		if (g_pClientSmsBuffer) 
		{
			UnmapViewOfFile(g_pClientSmsBuffer);
			g_pClientSmsBuffer = NULL;
		}
		if (g_hClientMMObj) 
		{
			CloseHandle(g_hClientMMObj);
			g_hClientMMObj = NULL;
		}
		if (g_pClientSmsqueue) {
			UnmapViewOfFile(g_pClientSmsqueue);
			g_pClientSmsqueue = NULL;
		}
		if(g_hClientMMObj_queue){
			CloseHandle(g_hClientMMObj_queue);
			g_hClientMMObj_queue = NULL;
		}
	}
	catch(...)
	{
		ErrorLog(L"Error TerminateSMSMessagePassing");
	}
}

EXTERN_C void ReleaseSmsEvent (void)
{
	// Make sure to have one last empty string available to copy to the client.
	//DebugOutput(L"Terminate SMS Message Called");
	try
	{
		g_pClientSmsBuffer = (PSMS_BUFFER)MapViewOfFile(g_hClientMMObj, FILE_MAP_WRITE, 0, g_pClientSmsqueue->start_pointer*sizeof(SMS_BUFFER), sizeof(SMS_BUFFER));
		memset(g_pClientSmsBuffer, 0, sizeof(SMS_BUFFER));
		SetEvent(g_hClientEvent);    // optionally allow the calling application to return from GetData.
		CloseHandle(g_hClientEvent);
		CloseHandle(g_hClientMutex);
		if (g_pClientSmsBuffer) 
		{
			UnmapViewOfFile(g_pClientSmsBuffer);
			g_pClientSmsBuffer = NULL;
		}
		if (g_hClientMMObj) 
		{
			CloseHandle(g_hClientMMObj);
			g_hClientMMObj = NULL;
		}
		if (g_pClientSmsqueue) {
			UnmapViewOfFile(g_pClientSmsqueue);
			g_pClientSmsqueue = NULL;
		}
		if(g_hClientMMObj_queue){
			CloseHandle(g_hClientMMObj_queue);
			g_hClientMMObj_queue = NULL;
		}
	}
	catch(...)
	{
		ErrorLog(L"Error TerminateSmsMessagePassing");
	}
}
